home *** CD-ROM | disk | FTP | other *** search
- /*
- * Blob Manager Demonstration: Peg Solitaire module
- *
- * 31 July 1986 Paul DuBois
- *
- * 23 Dec 93
- * - Made window a little wider so title in title bar doesn't get chopped.
- * 24 Dec 93
- * - Use RGB gray in final board positions rather than dithered gray when possible.
- * This uses something of a trick in that drawing such positions uses the functions
- * BeginBlobDimDraw() and EndBlobDimDraw() that are normally intended for use in
- * drawing dim blobs. But the final positions are active, not disabled, so
- * they're not really dim.
- */
-
- # include "TransSkel.h"
-
- # include "BlobMgr.h"
- # include "BlobDemo.h"
-
-
- # define rows 7 /* number of rows on board */
- # define columns 7 /* number of columns on board */
- # define pieceSize 20 /* size of each piece */
- # define vMessage 141 /* vertical position of message */
-
- /*
- * bits used in configuration information
- *
- * bit 0 0 board position unused
- * 1 board position used
- * bit 1 0 position does not have marble at start
- * 1 position has marble at start
- * bit 2 0 position does not have marble at end
- * 1 position has marble at end
- *
- * bits 1 and 2 are irrelevant unless bit 0 is on
- */
-
- # define posUsedMask 1
- # define inStartMask 2
- # define inFinalMask 4
-
-
- static WindowPtr wind;
- static MenuHandle cnfgMenu;
-
-
- static BlobSetHandle boardBlobs = nil; /* receptors */
- static BlobHandle board[columns][rows];
-
- static short hMid;
- static short cnfgNo; /* current configuration */
- static short moves; /* number of moves left */
- static Boolean pause;
- static Str255 statusStr = "\p";
-
-
- typedef struct
- {
- unsigned char *lName; /* name of configuration */
- char lCnfg[columns][rows]; /* board positions */
- } Layout;
-
-
- static Layout centerToCenter =
- {
- "\pCenter To Center",
- {
- { 0, 0, 3, 3, 3, 0, 0 },
- { 0, 0, 3, 3, 3, 0, 0 },
- { 3, 3, 3, 3, 3, 3, 3 },
- { 3, 3, 3, 5, 3, 3, 3 },
- { 3, 3, 3, 3, 3, 3, 3 },
- { 0, 0, 3, 3, 3, 0, 0 },
- { 0, 0, 3, 3, 3, 0, 0 }
- }
- };
-
-
- static Layout cornerToCorner =
- {
- "\pCorner To Corner",
- {
- { 0, 0, 1, 3, 3, 0, 0 },
- { 0, 3, 3, 3, 3, 3, 0 },
- { 3, 3, 3, 3, 3, 3, 3 },
- { 3, 3, 3, 3, 3, 3, 3 },
- { 3, 3, 3, 3, 3, 3, 3 },
- { 0, 3, 3, 3, 3, 3, 0 },
- { 0, 0, 3, 3, 7, 0, 0 }
- }
- };
-
-
- static Layout doubleCross =
- {
- "\pDouble Cross",
- {
- { 0, 0, 1, 3, 1, 0, 0 },
- { 0, 3, 1, 3, 1, 3, 0 },
- { 1, 1, 3, 3, 3, 1, 1 },
- { 3, 3, 3, 7, 3, 3, 3 },
- { 1, 1, 3, 3, 3, 1, 1 },
- { 0, 3, 1, 3, 1, 3, 0 },
- { 0, 0, 1, 3, 1, 0, 0 }
- }
- };
-
-
- static Layout finalScore =
- {
- "\pFinal Score",
- {
- { 0, 0, 7, 7, 7, 0, 0 },
- { 0, 7, 3, 3, 3, 7, 0 },
- { 7, 3, 7, 3, 7, 3, 7 },
- { 7, 3, 3, 5, 3, 3, 7 },
- { 7, 3, 3, 3, 3, 3, 7 },
- { 0, 7, 3, 7, 3, 7, 0 },
- { 0, 0, 7, 7, 7, 0, 0 }
- }
- };
-
-
- static Layout fiveCrosses =
- {
- "\pFive Crosses",
- {
- { 0, 0, 1, 3, 1, 0, 0 },
- { 0, 1, 3, 3, 3, 1, 0 },
- { 1, 3, 1, 3, 1, 3, 1 },
- { 3, 3, 3, 7, 3, 3, 3 },
- { 1, 3, 1, 3, 1, 3, 1 },
- { 0, 1, 3, 3, 3, 1, 0 },
- { 0, 0, 1, 3, 1, 0, 0 }
- }
- };
-
-
- static Layout footballTeam =
- {
- "\pFootball Team",
- {
- { 0, 0, 3, 3, 3, 0, 0 },
- { 0, 0, 3, 3, 3, 0, 0 },
- { 1, 1, 3, 3, 3, 1, 1 },
- { 1, 1, 3, 5, 3, 1, 1 },
- { 1, 1, 1, 1, 1, 1, 1 },
- { 0, 0, 1, 1, 1, 0, 0 },
- { 0, 0, 1, 1, 1, 0, 0 }
- }
- };
-
-
- static Layout greekCross =
- {
- "\pGreek Cross",
- {
- { 0, 0, 1, 1, 1, 0, 0 },
- { 0, 0, 1, 3, 1, 0, 0 },
- { 1, 1, 1, 3, 1, 1, 1 },
- { 1, 3, 3, 7, 3, 3, 1 },
- { 1, 1, 1, 3, 1, 1, 1 },
- { 0, 0, 1, 3, 1, 0, 0 },
- { 0, 0, 1, 1, 1, 0, 0 }
- }
- };
-
-
- static Layout latinCross =
- {
- "\pLatin Cross",
- {
- { 0, 0, 1, 1, 1, 0, 0 },
- { 0, 0, 1, 3, 1, 0, 0 },
- { 1, 1, 3, 3, 3, 1, 1 },
- { 1, 1, 1, 7, 1, 1, 1 },
- { 1, 1, 1, 3, 1, 1, 1 },
- { 0, 0, 1, 1, 1, 0, 0 },
- { 0, 0, 1, 1, 1, 0, 0 },
- }
- };
-
-
- static Layout leTricolet =
- {
- "\pLe Tricolet",
- {
- { 0, 0, 7, 3, 7, 0, 0 },
- { 0, 3, 3, 7, 3, 3, 0 },
- { 7, 3, 3, 7, 3, 3, 7 },
- { 3, 7, 7, 1, 7, 7, 3 },
- { 7, 3, 3, 7, 3, 3, 7 },
- { 0, 3, 3, 7, 3, 3, 0 },
- { 0, 0, 7, 3, 7, 0, 0 }
- }
- };
-
-
- static Layout lonelyCross =
- {
- "\pLonely Cross",
- {
- { 0, 0, 7, 7, 7, 0, 0 },
- { 0, 7, 3, 3, 3, 7, 0 },
- { 7, 3, 3, 7, 3, 3, 7 },
- { 7, 3, 7, 5, 7, 3, 7 },
- { 7, 3, 3, 7, 3, 3, 7 },
- { 0, 7, 3, 3, 3, 7, 0 },
- { 0, 0, 7, 7, 7, 0, 0 }
- }
- };
-
-
- static Layout octagon =
- {
- "\pOctagon",
- {
- { 0, 0, 1, 3, 1, 0, 0 },
- { 0, 3, 3, 3, 3, 3, 0 },
- { 1, 3, 3, 3, 3, 3, 1 },
- { 3, 3, 3, 7, 3, 3, 3 },
- { 1, 3, 3, 3, 3, 3, 1 },
- { 0, 3, 3, 3, 3, 3, 0 },
- { 0, 0, 1, 3, 1, 0, 0 }
- }
- };
-
-
- static Layout pentagon =
- {
- "\pPentagon",
- {
- { 0, 0, 1, 3, 1, 0, 0 },
- { 0, 0, 3, 3, 3, 0, 0 },
- { 1, 3, 3, 3, 3, 3, 1 },
- { 3, 3, 3, 7, 3, 3, 3 },
- { 1, 3, 3, 3, 3, 3, 1 },
- { 0, 0, 3, 3, 3, 0, 0 },
- { 0, 0, 1, 1, 1, 0, 0 }
- }
- };
-
-
- static Layout pyramidChefren =
- {
- "\pPyramid of Chefren",
- {
- { 0, 0, 1, 1, 1, 0, 0 },
- { 0, 1, 1, 1, 1, 1, 0 },
- { 1, 1, 1, 3, 1, 1, 1 },
- { 1, 1, 3, 7, 3, 1, 1 },
- { 1, 3, 3, 3, 3, 3, 1 },
- { 0, 1, 1, 1, 1, 1, 0 },
- { 0, 0, 1, 1, 1, 0, 0 },
- }
- };
-
-
- static Layout pyramidCheops =
- {
- "\pPyramid of Cheops",
- {
- { 0, 0, 1, 1, 1, 0, 0 },
- { 0, 1, 1, 3, 1, 1, 0 },
- { 1, 1, 3, 3, 3, 1, 1 },
- { 1, 3, 3, 7, 3, 3, 1 },
- { 3, 3, 3, 3, 3, 3, 3 },
- { 0, 1, 1, 1, 1, 1, 0 },
- { 0, 0, 1, 1, 1, 0, 0 },
- }
- };
-
-
- static Layout shrine =
- {
- "\pShrine",
- {
- { 0, 0, 1, 3, 1, 0, 0 },
- { 0, 1, 1, 3, 1, 1, 0 },
- { 1, 3, 3, 7, 3, 3, 1 },
- { 1, 1, 1, 3, 1, 1, 1 },
- { 1, 1, 1, 3, 1, 1, 1 },
- { 0, 1, 3, 3, 3, 1, 0 },
- { 0, 0, 3, 3, 3, 0, 0 },
- }
- };
-
-
- static Layout tiffanyLamp =
- {
- "\pTiffany Lamp",
- {
- { 0, 0, 1, 3, 1, 0, 0 },
- { 0, 0, 3, 3, 3, 0, 0 },
- { 1, 3, 3, 3, 3, 3, 1 },
- { 1, 1, 1, 7, 1, 1, 1 },
- { 1, 1, 1, 3, 1, 1, 1 },
- { 0, 0, 3, 3, 3, 0, 0 },
- { 0, 0, 3, 3, 3, 0, 0 },
- }
- };
-
-
- static Layout tiltedSquare =
- {
- "\pTilted Square",
- {
- { 0, 0, 1, 3, 1, 0, 0 },
- { 0, 0, 3, 3, 3, 0, 0 },
- { 1, 3, 3, 3, 3, 3, 1 },
- { 3, 3, 3, 5, 3, 3, 3 },
- { 1, 3, 3, 3, 3, 3, 1 },
- { 0, 0, 3, 3, 3, 0, 0 },
- { 0, 0, 1, 3, 1, 0, 0 }
- }
- };
-
-
- static Layout nullLayout =
- {
- nil
- };
-
-
- static Layout *configurations [] =
- {
- ¢erToCenter,
- &cornerToCorner,
- &doubleCross,
- &finalScore,
- &fiveCrosses,
- &footballTeam,
- &greekCross,
- &latinCross,
- &leTricolet,
- &lonelyCross,
- &octagon,
- &pentagon,
- &pyramidChefren,
- &pyramidCheops,
- &shrine,
- &tiffanyLamp,
- &tiltedSquare,
- &nullLayout
- };
-
-
- static void
- StatusMesg (StringPtr s)
- {
- Rect r;
-
- SetRect (&r, 0, vMessage, wind->portRect.right, vMessage + 12);
- TextBox (s+1, (long) s[0], &r, teJustCenter);
- StrCpy (statusStr, s);
- }
-
-
- /*
- * Board position drawing procedure. This draws a blank for those
- * positions that are unused. For used positions, the blob is filled
- * with light gray if a peg should be in the position at the end of
- * play. In the drag region, a hole is drawn if there is no glob,
- * otherwise a peg is drawn. It does not matter what glob is attached,
- * only whether there is one.
- *
- * partCode is ignored - always draw entire blob.
- */
-
- static pascal void
- DrawBoardPos (BlobHandle bDst, BlobHandle bSrc, short partCode)
- {
- short blobType;
- Rect r;
-
- blobType = GetBRefCon (bDst); /* indicates unused, used and present
- in final, or used and not present
- in final */
- r = BStatBox (bDst); /* blob outline */
- EraseRect (&r);
- if (blobType & posUsedMask)
- {
- if (blobType & inFinalMask)
- {
- BeginBlobDimDraw (bDst, partCode);
- PaintRect (&r);
- EndBlobDimDraw ();
- }
- if (BGlob (bDst) == nil) /* no peg, just draw hole */
- {
- InsetRect (&r, 7, 7);
- PaintOval (&r);
- }
- else
- {
- InsetRect (&r, 2, 2); /* draw peg */
- EraseOval (&r);
- PenSize (2, 2);
- FrameOval (&r);
- PenNormal ();
- }
- }
- }
-
-
- /*
- * Build the board. All positions are initially given a reference
- * value of 0, meaning an unused position. This means that the board
- * is blank until a configuration is set up. The blobs are built by
- * contructing the static and drag regions, without drawing anything,
- * since they are drawn by procedure.
- */
-
- static void
- InitBoard (void)
- {
- short h, v;
- Rect r, r2;
- BlobHandle b;
-
- boardBlobs = NewBlobSet ();
- for (v = 0; v < rows; v++)
- {
- for (h = 0; h < columns; h++)
- {
- b = NewBlob (boardBlobs, true, infiniteGlue, true, (long) 0);
- board[h][v] = b;
- SetRect (&r, 0, 0, pieceSize, pieceSize);
- OffsetRect (&r, h * pieceSize + 5, v * pieceSize);
- r2 = r;
- InsetRect (&r2, 2, 2);
- SetProcRectBlob (b, DrawBoardPos, &r2, &r);
- SetBlobFlags (b, bProcManualDimMask);
- }
- }
- }
-
-
- /*
- * Set the board to a given configuration. The first board blob is
- * used as the general glob and as the match for final positions.
- */
-
- static void
- SetupBoard (Layout *layout)
- {
- short h, v, val;
- short startCount = 0;
- short finalCount = 0;
- BlobHandle b;
- Str255 s;
-
- DisableBlobSet (boardBlobs); /* turn off drawing temporarily */
- for (v = 0; v < rows; v++)
- {
- for (h = 0; h < columns; h++)
- {
- b = board[h][v];
- val = (short) layout->lCnfg[v][h];
- SetBRefCon (b, val);
- UnglueGlob (b); /* clear any glob and match set */
- DisposeBlobMatchSet (b); /* from previous configuration */
- if (val & posUsedMask) /* check that position is used */
- {
- if (val & inStartMask) /* position has marble at start */
- {
- startCount++;
- GlueGlob (FirstBlob (boardBlobs), b);
- }
- if (val & inFinalMask) /* position has marble at end */
- {
- finalCount++;
- NewBlobMatch (FirstBlob (boardBlobs), b);
- }
- }
- }
- }
-
- ShowBlobSet (boardBlobs);
- moves = startCount - finalCount;
- MovesLeft (moves, s);
- StatusMesg (s);
- pause = false;
- }
-
-
- static void
- Pause (StringPtr msg)
- {
- StatusMesg (msg);
- pause = true;
- }
-
-
- /*
- * Check whether a board position is empty. The coordinates must be
- * legal, the position must be used in the current configuration,
- * and the position must have a marble in it.
- */
-
- static Boolean
- BoardPosEmpty (short h, short v)
- {
- return (h >= 0 && h < columns && v >= 0 && v < rows
- && (GetBRefCon (board[h][v]) & posUsedMask)
- && BGlob (board[h][v]) == nil);
- }
-
-
- static Boolean
- BoardPosFilled (short h, short v)
- {
- return (h >= 0 && h < columns && v >= 0 && v < rows
- && (GetBRefCon (board[h][v]) & posUsedMask)
- && BGlob (board[h][v]) != nil);
- }
-
-
- /*
- * Test whether a piece can move or not
- */
-
- static Boolean
- CanMove (short h, short v)
- {
- return ((BoardPosEmpty (h - 2, v) && BoardPosFilled (h - 1, v))
- || (BoardPosEmpty (h + 2, v) && BoardPosFilled (h + 1, v))
- || (BoardPosEmpty (h, v - 2) && BoardPosFilled (h, v - 1))
- || (BoardPosEmpty (h, v + 2) && BoardPosFilled (h, v + 1)));
- }
-
-
- /*
- * See if any more moves can be made. True if stalemated.
- */
-
- static Boolean
- HaveStaleMate (void)
- {
- BlobHandle b;
- short h, v;
-
- for (v = 0; v < rows; v++)
- {
- for (h = 0; h < columns; h++)
- {
- b = board[h][v];
- if (GetBRefCon (b) & posUsedMask)
- {
- if (BGlob (b) != nil && CanMove (h, v))
- return (false); /* at least 1 move can be made */
- }
- }
- }
- return (true);
- }
-
-
- /*
- * Given a blob handle, find the board position that corresponds to it.
- * This is used to map hits in the blob set (a list) to the position in
- * the board (a 2-d array).
- */
-
- static void
- FindBoardPos (BlobHandle b, short *h, short *v)
- {
- short i, j;
-
- for (i = 0; i < columns; ++i)
- {
- for (j = 0; j < rows; ++j)
- {
- if (board[i][j] == b)
- {
- *h = i;
- *v = j;
- return;
- }
- }
- }
- /* shouldn't ever get here */
- }
-
-
-
- /*
- * When a piece is clicked on, the advisory checks whether the piece has
- * any legal moves available to it. If so, it returns true, so that
- * BlobClick is allowed to drag the piece. After the piece has been
- * dragged, the advisory checks whether the position it was dragged to
- * is legal. If it is, the piece that was jumped is removed from the
- * board, and true is returned, so that BlobClick will complete the
- * tranfer of the dragged piece to its new position.
- *
- * Messages passed to the advisory follow the pattern
- *
- * { { advRClick advRClick* } advXfer* }*
- *
- * where * means 0 or more instances of the thing *'ed. In particular,
- * the advXfer message is never seen without a preceding advRClick.
- */
-
- static pascal Boolean
- Advisory (short mesg, BlobHandle b)
- {
- static short h, v; /* static to save board position of click on piece */
- short h2, v2;
-
- switch (mesg)
- {
-
- case advRClick: /* first click on piece */
-
- FindBoardPos (b, &h, &v); /* find where it is */
- return (CanMove (h, v));
-
- case advXfer: /* Mouse released after dragging piece */
-
- FindBoardPos (b, &h2, &v2);
- if ( ( (h2 == h - 2 && v2 == v)
- || (h2 == h + 2 && v2 == v)
- || (h2 == h && v2 == v - 2)
- || (h2 == h && v2 == v + 2) )
- && BoardPosFilled ((h + h2) / 2, (v + v2) / 2))
- {
- UnglueGlob (board[(h + h2) / 2][(v + v2) / 2]);
- return (true);
- }
- return (false);
-
- }
- }
-
-
- static pascal void
- DoConfiguration (short item)
- {
- CheckItem (cnfgMenu, cnfgNo + 1, false);
- SetupBoard (configurations [cnfgNo = item-1]);
- CheckItem (cnfgMenu, cnfgNo + 1, true);
- }
-
-
- static pascal void
- Mouse (Point pt, long t, short mods)
- {
- Str255 s;
- short i;
-
-
- if (!pause)
- {
- BlobClick (pt, t, nil, boardBlobs);
- if (BClickResult () == bcXfer)
- {
- --moves;
- if (BlobSetQuiet (boardBlobs))
- Pause ("\pYou Win");
- else if (moves == 0)
- Pause ("\pYou Lose");
- else if (HaveStaleMate ())
- Pause ("\pYou Can't Move");
- else
- {
- MovesLeft (moves, s);
- StatusMesg (s);
- }
- }
- }
- }
-
-
- static pascal void
- Update (Boolean resized)
- {
- DrawBlobSet (boardBlobs);
- StatusMesg (statusStr);
- }
-
-
- static pascal void
- Activate (Boolean active)
- {
- Layout *l;
-
- if (active)
- {
- SetDragRects (wind);
- SetBCPermissions (false, true, false, false, false); /* xfer only */
- SetBCAdvisory (Advisory);
- cnfgMenu = GetMenu (pegCnfgMenuRes);
- for (l = configurations[0]; l->lName != nil; ++l)
- {
- AppendMenu (cnfgMenu, l->lName);
- }
- SkelMenu (cnfgMenu, DoConfiguration, DoMClobber, false, true);
- CheckItem (cnfgMenu, cnfgNo+1, true);
- }
- else
- {
- SkelRmveMenu (cnfgMenu);
- SetBCAdvisory (nil);
- }
- }
-
-
- void
- PegInit (void)
- {
- Rect r;
-
- SkelWindow (wind = GetDemoWind (pegWindRes),
- Mouse, /* mouse clicks */
- nil, /* key clicks */
- Update, /* updates */
- Activate, /* activate/deactivate events */
- nil, /* close window */
- DoWClobber, /* dispose of window */
- nil, /* idle proc */
- false); /* irrelevant, since no idle proc */
-
- hMid = wind->portRect.right / 2;
-
- /*
- * Make blobs and set initial configuration to the Latin Cross, which
- * is good for learning some of the problems of peg solitaire.
- */
- InitBoard ();
- SetupBoard (configurations[cnfgNo = 7]);
-
- MakeFrontWind (wind);
- }
-